import re
import os
import threading
import simplejson

from odb.utils import logger

try:
    from fsevents import \
         IN_MODIFY, \
         IN_ATTRIB, \
         IN_CREATE, \
         IN_DELETE, \
         IN_MOVED_FROM, \
         IN_MOVED_TO
except ImportError:
    # TODO: import from ``inotify``
    pass

class Event(object):
    """File system event."""

    def __init__(self, mask, uri, cookie):
        self.mask = mask
        self.uri = uri
        self.cookie = cookie

    @property
    def share(self):
        return self.uri.split(':')[0]

class Observer(threading.Thread):
    """Common file system observer."""

    run = None
    buffer_size = 16384
    path_by_share = {}
    re_ignore = re.compile(r'.*?\.dropbox')
    _share_map = None

    def __init__(self):
        super(Observer, self).__init__()
        if self.run is None:
            raise ImportError(
                "Must have either ``MacFSEvents`` (on Mac OS X) or "
                "``pyinotify`` (Linux) to start observation.")

    def __del__(self):
        self.stop()

    def _build(self):
        # these two dictionaries are built once, and maintained by the
        # observer run loop
        _share_by_path = self._share_by_path = {}
        _path_by_share = self._path_by_share = {}

        for dirpath, dirnames, filenames in os.walk(self.path):
            for dirname in dirnames:
                path = os.path.join(dirpath, dirname)
                share = os.path.join(path, ".dropbox")
                if os.path.exists(share):
                    share_id = open(share).read()
                    _share_by_path[path] = share_id
                    _path_by_share[share_id] = path

    def resolve(self, uri):
        share, name = uri.split(":")
        return os.path.join(self._path_by_share[share], name)

    try:
        import fsevents
    except ImportError:
        pass
    else:
        def run(self):
            self._build()

            def callback(*file_events):
                events = []
                for event in file_events:
                    path, name = os.path.split(event.name)
                    if self.re_ignore.match(name):
                        continue
                    names = [name]

                    while path:
                        share_id = self._share_by_path.get(path)
                        if share_id is not None:
                            break
                        path, name = os.path.split(path)
                        names.append(name)
                    else:
                        continue

                    uri = "%s:%s" % (share_id, os.path.join(*reversed(names)))
                    event = Event(event.mask, uri, event.cookie)
                    events.append(event)

                if events:
                    for event in events:
                        try:
                            self.notify(event)
                        except NotImplementedError, e:
                            logger.critical(e)

            stream = self.fsevents.Stream(
                callback, self.path, file_events=True)
            observer = self.observer = self.fsevents.Observer()
            observer.schedule(stream)
            observer.run()
            observer.unschedule(stream)

        def stop(self):
            self.observer.stop()
            self.join()

    try:
        import pyinotify
    except ImportError:
        pass
    else:
        raise NotImplementedError(
            "Support for ``pyinotify``.")
